package grpcServer

import (
	"context"
	"google.golang.org/grpc"
	"google.golang.org/grpc/keepalive"
	"google.golang.org/grpc/metadata"
	"grpc.getaway.adminspro.padmins.com/pkg/libs/utils"
	"grpc.getaway.adminspro.padmins.com/pkg/support/log"
	"math/rand"
	"net"
	"strconv"
	"time"
)

// 为了保护服务端，防止恶意攻击或者防止客户端不去恰当的行为，对服务端造成破坏或性能受影响，服务端还针对keepalive设计了一个策略，叫 EnforcementPolicy，可以限制客户端ping的频率。
var keepaliveEnforcementPolicy = grpc.KeepaliveEnforcementPolicy(keepalive.EnforcementPolicy{
	MinTime:             5 * time.Second, // 如果客户端两次 ping 的间隔小于此值，则关闭连接
	PermitWithoutStream: true,            // 即使没有 active stream, 也允许 ping
})

var keepaliveParams = grpc.KeepaliveParams(keepalive.ServerParameters{
	MaxConnectionIdle:     15 * time.Second, // 如果一个 client 空闲超过该值, 发送一个 GOAWAY, 为了防止同一时间发送大量 GOAWAY, 会在此时间间隔上下浮动 10%, 例如设置为15s，即 15+1.5 或者 15-1.5
	MaxConnectionAge:      30 * time.Second, // 如果任意连接存活时间超过该值, 发送一个 GOAWAY
	MaxConnectionAgeGrace: 5 * time.Second,  // 在强制关闭连接之间, 允许有该值的时间完成 pending 的 rpc 请求
	Time:                  5 * time.Second,  // 如果一个 client 空闲超过该值, 则发送一个 ping 请求
	Timeout:               1 * time.Second,  // 如果 ping 请求该时间段内未收到回复, 则认为该连接已断开
})

// 前置拦截器
func beforeInterceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
	// 设置运行时间
	ctx = utils.WithExtraRuntime(ctx, utils.Runtime(time.Now()))

	// grpc服务端，header中metadata好像是存在context中的，从上下文中获取header中的值
	md, ok := metadata.FromIncomingContext(ctx)
	var traceId string
	if ok {
		traceIds, ok := md["trace-id"]
		if ok {
			traceId = traceIds[0]
		} else {
			traceId = utils.Md5(strconv.FormatInt(int64(rand.Intn(99999-10000)+10000), 10) + time.Now().String())
		}
	} else {
		traceId = utils.Md5(strconv.FormatInt(int64(rand.Intn(99999-10000)+10000), 10) + time.Now().String())
	}

	// 设置traceId
	ctx = utils.WithExtraTraceId(ctx, utils.TraceId(traceId))

	requestId := utils.Md5(strconv.FormatInt(int64(rand.Intn(99999-10000)+10000), 10) + time.Now().String())[:16]
	ctx = utils.WithExtraRequestId(ctx, utils.RequestId(requestId))
	data, e := handler(ctx, req)
	return data, e
}

// 后置拦截器
func alterInterceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
	logger := log.WithContext(ctx)

	data, e := handler(ctx, req)
	if e != nil {
		logger.Error("grpc错误 "+e.Error(), data)
	} else {
		logger.Info("grpc成功", data)
	}
	traceId, _ := utils.ExtraTraceId(ctx)
	grpc.SetHeader(ctx, metadata.Pairs("trace-id", string(traceId)))
	return data, e
}

func NewServer(RegisterName, address string, registerFunc func(server *grpc.Server)) error {
	listener, err := net.Listen("tcp", address)
	if err != nil {
		panic("服务监听端口失败" + err.Error())
	}

	var interceptor = func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
		// grpc服务端，header中metadata好像是存在context中的，从上下文中获取header中的值
		ctx = log.WithExtraLog(ctx, "grpc-"+RegisterName)
		md, _ := metadata.FromIncomingContext(ctx)
		logger := log.WithContext(ctx)
		logger.Info("请求参数", map[string]interface{}{
			"header": md,
			"req":    req,
		})
		data, e := handler(ctx, req)
		return data, e
	}

	opts := []grpc.ServerOption{
		grpc.ChainUnaryInterceptor(beforeInterceptor, interceptor, alterInterceptor),
		keepaliveEnforcementPolicy,
		keepaliveParams,
	}
	rpcServer := grpc.NewServer(opts...)
	defer rpcServer.GracefulStop()
	registerFunc(rpcServer)
	return rpcServer.Serve(listener)
}
